package com.fanrenqiji.common.service;

import com.fanrenqiji.common.repository.DefaultSearchCallback;
import com.fanrenqiji.common.search.Searchable;
import com.fanrenqiji.common.search.filter.SearchFilterHelper;
import com.fanrenqiji.common.utils.Reflections;
import com.google.common.collect.Lists;
import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>抽象service层基类 提供一些简便方法
 * <p/>
 * <p>泛型 ： M 表示实体类型；V表示VO类型
 * <p/>
 */
public class BaseService<V, M> implements IBaseService<V> {


    @PersistenceContext
    private EntityManager em;

    private DefaultSearchCallback searchCallback = new DefaultSearchCallback();


    protected final Class<V> voClass;
    protected final Class<M> entityClass;

    public BaseService() {
        Class<V> voClass = Reflections.findParameterizedType(getClass(), 0);
        Class<M> entityClass = Reflections.findParameterizedType(getClass(), 1);
        this.voClass = voClass;
        this.entityClass = entityClass;
    }


    @Autowired
    protected Mapper mapper;

    /**
     * 保存单个实体
     *
     * @param v 实体
     * @return 返回保存的实体
     */
    @Transactional
    public V saveOrUpdate(V v) {
        M m = null;
        try {
            m = entityClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        mapper.map(v, m);
        if (Reflections.getFieldValue(v, "id") != null) {
            M db = em.find(entityClass, Long.parseLong("" + Reflections.getFieldValue(v, "id")));
            if (db != null) {
                mapper.map(v, db);
                m = db;
            }
        }
        em.persist(m);
        mapper.map(m, v);
        return v;
    }

    /**
     * 根据主键删除相应实体
     *
     * @param id 主键
     */
    @Transactional
    public void delete(String id) {
        em.remove(em.find(entityClass, Long.parseLong(id)));
    }


    /**
     * 根据主键删除相应实体
     *
     * @param ids 实体
     */
    @Transactional
    public void delete(String[] ids) {

        for (String id : ids) {
            delete(id);
        }
    }


    /**
     * 按照主键查询
     *
     * @param id 主键
     * @return 返回id对应的实体
     */
    @Transactional(readOnly = true)
    public V findOne(String id) {
        M m = em.find(entityClass, Long.parseLong(id));
        V v = null;
        try {
            v = voClass.newInstance();
            if (m != null) {
                mapper.map(m, v);
            }
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return v;
    }

    /**
     * 实体是否存在
     *
     * @param id 主键
     * @return 存在 返回true，否则false
     */
    @Transactional(readOnly = true)
    public boolean exists(String id) {
        M m = em.find(entityClass, Long.parseLong(id));
        return m == null ? false : true;
    }


    public long count(final Searchable searchable) {

        searchable.addSearchFilter(SearchFilterHelper.newCondition("delFlag_eq", 0));

        StringBuilder s = new StringBuilder("select count(x) from " + entityClass.getName() + " x");
        searchCallback.prepareQL(s, searchable);
        Query query = em.createQuery(s.toString());
        searchCallback.setValues(query, searchable);

        return (Long) query.getSingleResult();
    }


    /**
     * 按条件不分页不排序查询实体
     *
     * @param searchable 条件
     * @return
     */
    @Transactional(readOnly = true)
    public List<V> findAllWithNoPageNoSort(Searchable searchable) {
        searchable.removePageable();
        searchable.removeSort();
        return Lists.newArrayList(searchAll(searchable).getContent());
    }

    /**
     * 按条件不分页查询实体
     *
     * @param searchable 条件
     * @return
     */
    @Transactional(readOnly = true)
    public List<V> findAllWithNoPage(Searchable searchable) {
        searchable.removePageable();
        return Lists.newArrayList(searchAll(searchable).getContent());
    }

    /**
     * 按条件分页并排序查询实体
     *
     * @param searchable 条件
     * @return
     */
    @Transactional(readOnly = true)
    public Page<V> searchAll(Searchable searchable) {

        searchable = searchable.addSearchFilter(SearchFilterHelper.newCondition("delFlag_eq", 0));

        StringBuilder s = new StringBuilder("from " + entityClass.getName() + " x");
        searchCallback.prepareQL(s, searchable);
        searchCallback.prepareOrder(s, searchable);
        Query query = em.createQuery(s.toString());
        searchCallback.setValues(query, searchable);
        searchCallback.setPageable(query, searchable);

        List<M> page = query.getResultList();
        List<V> list = Lists.newArrayList();

        for (M m : page) {
            V v = null;
            try {
                v = voClass.newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            mapper.map(m, v);
            list.add(v);
        }

        long total = searchable.hasPageable() ? count(searchable) : list.size();
        return new PageImpl<V>(
                list,
                searchable.getPage(),
                total
        );
    }

}
